package thinkinginjava.concurrency;

import java.util.concurrent.Callable;
import java.util.concurrent.CancellationException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Future;
import java.util.concurrent.FutureTask;

import org.apache.commons.io.DirectoryWalker.CancelException;

public class Memoizer<K, V> implements Computable<K, V>{
	private final ConcurrentHashMap<K, Future<V>> cache = new ConcurrentHashMap<K, Future<V>>();
	private final Computable<K, V> c;
	
	public Memoizer(Computable<K, V> c) {
		super();
		this.c = c;
	}

	@Override
	public V compute(final K arg) throws InterruptedException {
		while(true) {
			Future<V> f = cache.get(arg);
			Callable<V> eval = new Callable<V>() {

				@Override
				public V call() throws Exception {
					return c.compute(arg);
				}
				
			};
			FutureTask<V> ft = new FutureTask<V>(eval);
			f = cache.putIfAbsent(arg, f);
			if(f == null) {
				f = ft;
				ft.run();
			}
			
			try {
				return f.get();
			} catch (CancellationException ce) {
				cache.remove(arg);
			} catch (ExecutionException e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
			
		}
	}

}
